/**
 * This file is part of Lineage II Client Data Parser.
 * 
 * Lineage II Client Data Parser is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Lineage II Client Data Parser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Lineage II Client Data Parser.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.l2emuproject.parser;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import net.l2emuproject.Launcher;
import net.l2emuproject.entity.ClanBase;
import net.l2emuproject.io.FileManager;
import net.l2emuproject.io.SafelyPackedFile;
import net.l2emuproject.parser.base.DefaultParser;
import net.l2emuproject.redist.UnsupportedException;

import javolution.text.TextBuilder;

/**
 * @author Savormix
 *
 */
public final class ClanBaseParser extends DefaultParser
{
	private static final String[]	NEEDED_FILES	=
													{ Launcher.FILES[2] };

	private final List<ClanBase>	entities;

	/**
	 * Creates a parser that parses clan base data. Needed Files:
	 * <LI>castlename-e</LI>
	 * @param spf File array
	 * @throws IllegalArgumentException Wrong file(s) 
	 */
	public ClanBaseParser(SafelyPackedFile... spf) throws IllegalArgumentException
	{
		super(spf);
		entities = new ArrayList<ClanBase>(100);
	}

	/** Creates a parser that validates files */
	public ClanBaseParser()
	{
		super();
		entities = null;
	}

	private boolean parseCastleName(int entryCnt, SafelyPackedFile spf)
	{
		if (!isValidator())
			log.info("Reading " + entryCnt + " entries from " + spf.getFileName());
		for (int i = 0; i < entryCnt; i++)
		{
			if (isValidator())
				return spf.readInt() == 0;
			int elem = spf.readInt(), tag = spf.readInt(), id = spf.readInt();
			String name = spf.readString(), loc = spf.readString(), desc = spf.readString();
			String icon = EMPTY_STRING, pIcon = EMPTY_STRING, gIcon = EMPTY_STRING, guard = EMPTY_STRING;
			if (spf.getChronicleID() > 10)
			{
				int ter = spf.readUnsignedByte();
				if (ter > 0)
				{
					icon = spf.readString(ter);
					pIcon = spf.readString();
					gIcon = spf.readString();
					guard = spf.readString();
				}
				else
					spf.movePointer(3);
			}
			entities.add(new ClanBase(elem, tag, id, name, loc, desc, icon, pIcon, gIcon, guard));
		}
		log.info("Finished reading " + spf.getFileName());
		return false;
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#getNeededFiles()
	 */
	@Override
	public String[] getNeededFiles()
	{
		return NEEDED_FILES;
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#getParseType()
	 */
	@Override
	public String getParseType()
	{
		return "clan base";
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#readClientData()
	 */
	@Override
	public void readClientData() throws IllegalAccessException
	{
		if (isValidator())
			throw new IllegalAccessException(toString());
		parseCastleName(entries[0], spf[0]);
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#selfAdvancedAnalysis()
	 */
	@Override
	public void selfAdvancedAnalysis() throws IllegalAccessException
	{
		if (isValidator())
			throw new IllegalAccessException(toString());
		for (ClanBase cb : entities)
		{
			if (ANALYZE_DETERMINISTIC)
				cb.deterministicAnalysis();
			if (ANALYZE_EXPERIMENTAL)
				cb.experimentalAnalysis();
		}
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#validate(com.l2cdp.entity.SafelyPackedFile)
	 */
	@Override
	public boolean validate(SafelyPackedFile spf)
	{
		try
		{
			return parseCastleName(spf.readInt(), spf);
		}
		catch (Exception e)
		{
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#writeServerData(int)
	 */
	@Override
	public void writeServerData(int chronicle) throws IllegalAccessException
	{
		if (isValidator())
			throw new IllegalAccessException(toString());
		synchronized (finLock)
		{
			addThread();
		}
		new Thread(new AsyncWriterCBSrv(chronicle), AsyncWriterCBSrv.class.getName()).start();
	}

	/* (non-Javadoc)
	 * @see com.l2cdp.parser.IParser#writeSimpleData(int)
	 */
	@Override
	public void writeSimpleData(int chronicle) throws IllegalAccessException
	{
		if (isValidator())
			throw new IllegalAccessException(toString());
		synchronized (finLock)
		{
			addThread();
		}
		new Thread(new AsyncWriterCBSim(chronicle), AsyncWriterCBSim.class.getName()).start();
	}

	protected class AsyncWriterCBSim implements Runnable
	{
		private final int	cID;

		protected AsyncWriterCBSim(int cID)
		{
			this.cID = cID;
		}

		private void appendMainProps(TextBuilder sb, ClanBase cb)
		{
			sb.append(cb.getId());
			sb.append(EQUAL);
			sb.append(cb.getElement());
			sb.append(COMMA);
			sb.append(cb.getTag());
			sb.append(SEMICOLON);
			sb.append(cb.getName());
			sb.append(COMMA);
			sb.append(cb.getLoc());
			if (!cb.getDesc().isEmpty())
			{
				sb.append(COMMA);
				sb.append(cb.getDesc());
			}
		}

		/* (non-Javadoc)
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run()
		{
			TextBuilder[] sb = new TextBuilder[]
			{ TextBuilder.newInstance(), TextBuilder.newInstance(), TextBuilder.newInstance(), TextBuilder.newInstance(), };
			try
			{
				Writer[] w = new Writer[]
				{
						FileManager.getWriter(cID, "castle.properties", true),
						FileManager.getWriter(cID, "hideout.properties", true),
						FileManager.getWriter(cID, "fortress.properties", true),
						FileManager.getWriter(cID, "territory.properties", true) };
				for (int i = 0; i < sb.length; i++)
				{
					addGenInfo(cID, sb[i], COMMENT_PROP);
				}

				for (ClanBase cb : entities)
				{
					if (cb.isCastle())
					{
						appendMainProps(sb[0], cb);
						sb[0].append(LINE_BREAK);
					}
					else if (cb.isHideout())
					{
						appendMainProps(sb[1], cb);
						sb[1].append(SEMICOLON);
						sb[1].append(cb.getLease());
						sb[1].append(COMMA);
						sb[1].append(cb.getGrade());
						sb[1].append(LINE_BREAK);
					}
					else if (cb.isFortress())
					{
						appendMainProps(sb[2], cb);
						sb[2].append(SEMICOLON);
						if (cb.isBorder())
							sb[2].append("border");
						else
							sb[2].append("non-border");
						sb[2].append(COMMA);
						sb[2].append(cb.getBarType());
						sb[2].append(LINE_BREAK);
					}
					else if (cb.isTerritory())
					{
						appendMainProps(sb[3], cb);
						sb[3].append(SEMICOLON);
						sb[3].append(cb.getIcon());
						sb[3].append(SEMICOLON);
						sb[3].append(cb.getPenaltyIcon());
						sb[3].append(SEMICOLON);
						sb[3].append(cb.getGuideIcon());
						sb[3].append(SEMICOLON);
						sb[3].append(cb.getGuardian());
						sb[3].append(LINE_BREAK);
					}
				}

				for (int i = 0; i < sb.length; i++)
				{
					addGenInfo(-1, sb[i], COMMENT_PROP);
					w[i].write(sb[i].toString());
					w[i].close();
				}
			}
			catch (UnsupportedException e)
			{
				log.fatal("Unsupported chronicle, will not write data!", e);
			}
			catch (IOException e)
			{
				log.error("Cannot write clan base properties!", e);
			}
			finally
			{
				for (TextBuilder tb : sb)
					TextBuilder.recycle(tb);
				synchronized (finLock)
				{
					remThread();
				}
			}
		}
	}

	protected class AsyncWriterCBSrv implements Runnable
	{
		private final int	cID;

		protected AsyncWriterCBSrv(int cID)
		{
			this.cID = cID;
		}

		/* (non-Javadoc)
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run()
		{
			TextBuilder[] sb = new TextBuilder[]
			{ TextBuilder.newInstance(), TextBuilder.newInstance(), TextBuilder.newInstance(), };
			try
			{
				Writer[] w = new Writer[]
				{
						FileManager.getWriter(cID, "castle.sql", false),
						FileManager.getWriter(cID, "clanhall.sql", false),
						FileManager.getWriter(cID, "fort.sql", false) };
				for (int i = 0; i < sb.length; i++)
				{
					addGenInfo(cID, sb[i], COMMENT_SQL);
				}
				sb[0].append("INSERT IGNORE INTO `castle` (`id`, `name`) VALUES");
				sb[0].append(LINE_BREAK);
				sb[1].append("INSERT IGNORE INTO `clanhall` (`id`, `name`, `lease`, `desc`, `location`, `Grade`) VALUES");
				sb[1].append(LINE_BREAK);
				sb[2].append("INSERT IGNORE INTO `fort` (`id`, `name`, `fortType`) VALUES");
				sb[2].append(LINE_BREAK);

				for (ClanBase cb : entities)
				{
					if (cb.isCastle())
					{
						sb[0].append(PAREN_OPEN);
						sqlDeclare(sb[0], cb.getId());
						sqlDeclare(sb[0], cb.getLoc(), true);
					}
					else if (cb.isHideout())
					{
						sb[1].append(PAREN_OPEN);
						sqlDeclare(sb[1], cb.getId());
						sqlDeclare(sb[1], cb.getName());
						sqlDeclare(sb[1], cb.getLease());
						sqlDeclare(sb[1], cb.getDesc());
						sqlDeclare(sb[1], cb.getLoc());
						sqlDeclare(sb[1], cb.getGrade(), true);
					}
					else if (cb.isFortress())
					{
						sb[2].append(PAREN_OPEN);
						sqlDeclare(sb[2], cb.getId());
						sqlDeclare(sb[2], cb.getName().split(" ")[0]);
						sqlDeclare(sb[2], cb.getBarType(), true);
					}
				}

				for (int i = 0; i < sb.length; i++)
				{
					sqlFinishQuery(sb[i]);
					w[i].write(sb[i].toString());
					w[i].close();
				}
			}
			catch (UnsupportedException e)
			{
				log.fatal("Unsupported chronicle, will not write data!", e);
			}
			catch (IOException e)
			{
				log.error("Cannot write clan base server data!", e);
			}
			finally
			{
				for (TextBuilder tb : sb)
					TextBuilder.recycle(tb);
				synchronized (finLock)
				{
					remThread();
				}
			}
		}
	}
}
